package com.http;

import com.FileBehavior.FileHelp;
import com.FileBehavior.FileIO;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.util.SQLHelp;

import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;

public class BaiduPanConfig {
    public static String UserInfoUrl = "https://pan.baidu.com/rest/2.0/xpan/nas?method=uinfo";
    public static String FileListUrl = "https://pan.baidu.com/rest/2.0/xpan/file?method=list";
    public static String CreateFolderUrl = "https://pan.baidu.com/rest/2.0/xpan/file?method=create";
    public static String PrecreateUrl = "https://pan.baidu.com/rest/2.0/xpan/file?method=precreate";
    public static String UploadFileUrl = "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2?method=upload";
    public static String CreateFileUrl = "https://pan.baidu.com/rest/2.0/xpan/file?method=create";
    public static String DeleteFileUrl = "https://pan.baidu.com/rest/2.0/xpan/file?method=filemanager";
    public static String FileMetasUrl = "https://pan.baidu.com/rest/2.0/xpan/multimedia?method=filemetas";
    public static String FileListAllUrl = "https://pan.baidu.com/rest/2.0/xpan/multimedia?method=listall";

    public static String getPanUserInfoUrl(String accessToken){
        return String.format("%s&access_token=%s", UserInfoUrl, accessToken);
    }

    public static String getFileListUrl(String accessToken, String fileUrl){
        return String.format("%s&access_token=%s&dir=/apps/new_folder/%s", FileListUrl, accessToken, fileUrl);
    }

    public static String getCreateFolderUrl(String accessToken){
        return String.format("%s&access_token=%s", CreateFolderUrl, accessToken);
    }

    public static String getPrecreateUrl(String accessToken){
        return String.format("%s&access_token=%s", PrecreateUrl, accessToken);
    }

    public static String getUploadFileUrl(String accessToken, String panFilePath, String uploadid, String partseq){
        return String.format("%s&access_token=%s&type=tmpfile&path=%s&uploadid=%s&partseq=%s", UploadFileUrl, accessToken, panFilePath, uploadid, partseq);
    }

    public static String getCreateFileUrl(String accessToken){
        return String.format("%s&access_token=%s", CreateFileUrl, accessToken);
    }

    public static String getDeleteFileUrl(String accessToken){
        return String.format("%s&access_token=%s&opera=delete", DeleteFileUrl, accessToken);
    }

    public static String getFileMetasUrl(String accessToken, String fsids){
        return String.format("%s&access_token=%s&fsids=%s&dlink=1", FileMetasUrl, accessToken, fsids);
    }

    public static String getFileListAllUrl(String accessToken, String fileUrl){
        int limit = 10000;
        return String.format("%s&access_token=%s&path=%s&recursion=1&limit=%s", FileListAllUrl, accessToken, fileUrl, limit);
    }

    public static boolean createAppFolder(String accessToken) throws UnsupportedEncodingException {
        // 文件夹的路径
        // /apps/new_folder
        String panFolder = "/apps/new_folder";

        // 创建文件夹
        JSONObject body = new JSONObject();
        //body.put("path", panFolder);
        body.put("path", URLEncoder.encode(panFolder, "UTF-8"));
        body.put("isdir", "1");
        // 不覆盖
        body.put("rtype", "0");
        String createUrl = BaiduPanConfig.getCreateFolderUrl(accessToken);

        JSONObject result = HttpRequest.PostRequestFormUrlencoded(createUrl, body);
        if(result.get("errno").equals(-7) || result.get("errno").equals(-10)){
            System.out.println("创建文件夹失败:" + body.toJSONString() + ";"  + result.toJSONString());
            return false;
        }
        // 这里已经创建文件夹成功了
        return true;
    }

    public static boolean createFolder(String accessToken, String folderKey) throws UnsupportedEncodingException {
        // 文件夹的路径
        // /apps/new_folder/folder_key
        String panFolderFormat = "/apps/new_folder/%s";
        String panFolder = String.format(panFolderFormat, folderKey);

        // 创建文件夹
        JSONObject body = new JSONObject();
        //body.put("path", panFolder);
        body.put("path", URLEncoder.encode(panFolder, "UTF-8"));
        body.put("isdir", "1");
        // 不覆盖
        body.put("rtype", "0");
        String createUrl = BaiduPanConfig.getCreateFolderUrl(accessToken);

        JSONObject result = HttpRequest.PostRequestFormUrlencoded(createUrl, body);
        if(result.get("errno").equals(-7) || result.get("errno").equals(-10)){
            System.out.println("创建文件夹失败:" + body.toJSONString() + ";"  + result.toJSONString());
            return false;
        }
        // 这里已经创建文件夹成功了
        return true;
    }


    public static boolean uploadFile(String accessToken, String folderKey, String filePath) throws IOException {
        String absoPath = FileHelp.relaPathToAbsoPath(filePath);
        File file = new File(absoPath);
        if(!file.exists()){
            return false;
        }
        // /apps/new_folder/folder_key
        String panFolderFormat = "/apps/new_folder/%s/%s";
        String fileName = file.getName();
        String panFilePath = String.format(panFolderFormat, folderKey, fileName);
        UploadFileHelp uploadFileHelp = new UploadFileHelp(panFilePath, accessToken, file);
        uploadFileHelp.preprocess();
        if(!uploadFileHelp.precreate()){
            return false;
        }
        if(!uploadFileHelp.upload()){
            return false;
        }
        if(!uploadFileHelp.combine()){
            return false;
        }
        uploadFileHelp.delete();
        return true;
    }


    public static boolean deleteFile(String accessToken, String folderKey, String[] fileNames) throws IOException {
        // /apps/new_folder/folder_key
        if(fileNames.length == 0){
            return true;
        }
        String panFolderFormat = "/apps/new_folder/%s/%s";
        for(int i=0; i < fileNames.length; ++i){
            String fileName = fileNames[i];
            String panFilePath = String.format(panFolderFormat, folderKey, fileName);
            fileNames[i] = panFilePath;
        }
        String filelist = FileHelp.Strings2JSON(fileNames);
        JSONObject body = new JSONObject();
        body.put("filelist", filelist);
        body.put("async", 1);
        JSONObject result = HttpRequest.PostRequest(getDeleteFileUrl(accessToken), body);
        if(!result.get("errno").equals(0)){
            System.out.println("删除文件失败:" + body.toJSONString() + ";" + result.toJSONString());
            return false;
        }return true;
    }

    public static String previewFile(String accessToken, String fsids, String fileName) throws IOException {
        PreviewFileHelp previewFileHelp = new PreviewFileHelp(accessToken, fsids, fileName);
        previewFileHelp.getDLink();
        previewFileHelp.startDownload();
        return previewFileHelp.getFilePath();
    }


    public static HashMap<String, Integer> getFileListAll(String userId) throws IOException {
        String panFolder = "/apps/new_folder";
        String sqlFormat = "select * from baiduoauthtable where user_id = %s";
        String sql = String.format(sqlFormat, userId);
        // 可能有多个网盘，那就需要多线程查询
        ArrayList<HashMap<String, String>> res = SQLHelp.selectQuery(sql);
        if(res.isEmpty()){
            return new HashMap<>();
        }
        // 多线程
        // https://blog.csdn.net/weixin_39448458/article/details/100718189
        int threadNum = res.size();
        //创建一个CountDownLatch类，构造入参线程数
        CountDownLatch countDownLatch = new CountDownLatch(threadNum);

        ArrayList<HttpThreadHelp> threadList = new ArrayList<>();
        for(HashMap<String, String> h: res){
            String accessToken = h.get("access_token");
            accessToken = BaiduOAuthConfig.checkAndGetPanAccessToken(accessToken);
            if(accessToken.equals("")){
                countDownLatch.countDown();
            }
            JSONObject headers = new JSONObject();
            headers.put("User-Agent", "pan.baidu.com");
            String url = getFileListAllUrl(accessToken, URLEncoder.encode(panFolder, "UTF-8"));
            HttpThreadHelp t = new HttpThreadHelp(url, HttpThreadHelp.GetWithHead, countDownLatch, headers, null);
            threadList.add(t);
            t.start();
        }
        try{
            //下面这句代码，CountDownLatch就阻塞在这里了
            //直到countDown()到0了（从构造入参的线程数开始减）
            //也即是所有线程都countDown了，
            //则解除阻塞，代码继续往下执行
            //注意，这句代码放在多线程countDown之后，多线程全部完成后
            //继续往下执行的代码之前，起一个分界线的作用
            countDownLatch.await();
            HashMap<String, Integer> folderKeyFilesNums = new HashMap<>();
            for(HttpThreadHelp t: threadList){
                if(!t.result.get("errno").equals(0)){
                    // 出错了
                    continue;
                }
                JSONArray folders = (JSONArray)t.result.get("list");
                for (Object folder:folders){
                    JSONObject o = (JSONObject) JSON.toJSON(folder);
                    if(o.get("isdir").equals(1)){
                        // 目录
                        continue;
                    }
                    String path = o.get("path").toString();
                    String folderKey = path.substring(17, 27);
                    if(folderKeyFilesNums.containsKey(folderKey)){
                        folderKeyFilesNums.put(folderKey, folderKeyFilesNums.get(folderKey) + 1);
                    }else{
                        folderKeyFilesNums.put(folderKey, 1);
                    }
                }
            }
            return folderKeyFilesNums;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

class HttpThreadHelp extends Thread{
    public String url;
    public JSONObject head;
    public JSONObject body;
    public JSONObject result;
    public int type;
    public CountDownLatch countDownLatch;

    public static int GetWithNone = 0;
    public static int GetWithHead = 1;
    public static int GetWithBody = 2;
    public static int GetWithHeadBody = 3;
    public static int PostWithNone = 4;
    public static int PostWithHead = 5;
    public static int PostWithBody = 6;
    public static int PostWithHeadBody = 7;
    public static int PostFormUrlencoded = 99;

    public HttpThreadHelp(String url, int type, CountDownLatch countDownLatch, JSONObject head, JSONObject body){
        this.url = url;
        this.head = head;
        this.body = body;
        this.type = type;
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        super.run();
        try{
            if(type == GetWithNone){
                result = HttpRequest.GetRequest(url);
            }
            else if(type == GetWithHead){
                result = HttpRequest.GetRequestHead(url, head);
            }
            else if(type == PostWithNone){
                result = HttpRequest.PostRequest(url);
            }
            else if(type == PostWithBody){
                result = HttpRequest.PostRequest(url, body);
            }
            else if(type == PostFormUrlencoded){
                result = HttpRequest.PostRequestFormUrlencoded(url, body);
            }
            else{
                throw new RuntimeException(new RuntimeException("type error"));
            }
            countDownLatch.countDown();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

class UploadFileHelp{
    public String panFilePath;
    public String uploadid;
    public String accessToken;
    public String[] blockList;
    // 当前的文件，分片前的，大文件
    public File file;
    public long size;
    // 分片后的，如果没有分片，那就是自身
    public ArrayList<File> fileList;

    public File dir;

    public UploadFileHelp(String _panFilePath, String _accessToken, File _file){
        panFilePath = _panFilePath;
        accessToken = _accessToken;
        file = _file;
        size = file.length();
    }

    public void preprocess() throws IOException {
        if(size <= 4 * 1024 * 1024){
            // 不用分片
            blockList = new String[1];
            blockList[0] = FileHelp.getFileMD5(file);
            fileList = new ArrayList<>();
            fileList.add(file);
        }else{
            String dirPath = FileIO.splitFile(file.getAbsolutePath());
            dir = new File(dirPath);
            fileList = FileIO.getOrderFileList(dirPath);
            blockList = new String[fileList.size()];
            for(int i = 0; i < fileList.size(); ++i){
                String md5 = FileHelp.getFileMD5(fileList.get(i));
                blockList[i] = md5;
            }
        }
    }

    public void setUploadid(String d){
        uploadid = d;
    }

    public boolean precreate() throws UnsupportedEncodingException {
        JSONObject body = new JSONObject();
        body.put("path", URLEncoder.encode(panFilePath, "UTF-8"));
        body.put("size", size);
        body.put("isdir", 0);
        body.put("block_list", FileHelp.Strings2JSON(blockList));
        body.put("autoinit", 1);
        JSONObject result = HttpRequest.PostRequestFormUrlencoded(BaiduPanConfig.getPrecreateUrl(accessToken), body);
        if(result.containsKey("errno") && !result.get("errno").equals(0)){
            System.out.println("预创建文件失败:" + body.toJSONString() + ";"  + result.toJSONString());
            return false;
        }
        setUploadid((String) result.get("uploadid"));
        return true;
    }

    public boolean upload() throws IOException {
        for(Integer i = 0; i < fileList.size(); ++i){
            File file = fileList.get(i);
            String url = BaiduPanConfig.getUploadFileUrl(accessToken, URLEncoder.encode(panFilePath, "UTF-8"), uploadid, i.toString());
            JSONObject result = HttpRequest.uploadFile(url, file);
            if(result.containsKey("errno") && !result.get("errno").equals(0)){
                System.out.println("上传文件失败:" + result.toJSONString());
                return false;
            }
            //System.out.println("上传分片成功" + (i + 1) + "/" + fileList.size());
        }
        //System.out.println("上传成功");
        return true;
    }

    public boolean combine() throws UnsupportedEncodingException {
        JSONObject body = new JSONObject();
        body.put("path", URLEncoder.encode(panFilePath, "UTF-8"));
        body.put("size", size);
        body.put("isdir", 0);
        body.put("block_list", FileHelp.Strings2JSON(blockList));
        body.put("uploadid", uploadid);
        JSONObject result = HttpRequest.PostRequestFormUrlencoded(BaiduPanConfig.getCreateFileUrl(accessToken), body);
        if(result.containsKey("errno") && !result.get("errno").equals(0)){
            System.out.println("创建文件失败:" + body.toJSONString() + ";"  + result.toJSONString());
            return false;
        }
        return true;
    }

    public void delete(){
        // 删除文件
        for(File f:fileList){
            if(f.exists()){
                f.delete();
            }
        }
        if(file.exists()){
            file.delete();
        }
        if(dir != null && dir.exists()){
            dir.delete();
        }
    }

}

class PreviewFileHelp{
    String fsids;
    String accessToken;
    String fileName;

    String localPath="";

    long querySeconds=0;

    boolean isOk = false;

    String dlink="";

    public PreviewFileHelp(String _accessToken, String _fsids, String _fileName) throws IOException {
        accessToken = _accessToken;
        fsids = String.format("[%s]", _fsids);
        fileName = _fileName;
        File file = new File(FileIO.getDownloadFilesPath() + "/" + fileName);
        if(file.exists()){
            isOk = true;
            localPath = FileHelp.absoPathToRelaPath(file.getAbsolutePath());
        }
    }

    public void getDLink() throws IOException {
        if(isOk){
            return;
        }
        String url = BaiduPanConfig.getFileMetasUrl(accessToken, fsids);
        JSONObject headers = new JSONObject();
        headers.put("User-Agent", "pan.baidu.com");
        JSONObject result = HttpRequest.GetRequestHead(url, headers);
        if(!result.get("errno").equals(0)){
            System.out.println("getDlink失败" + result.toJSONString());
            return;
        }
        JSONArray files = (JSONArray)result.get("list");
        if(files.size() == 0){
            System.out.println("getDlink失败" + result.toJSONString());
            return;
        }
        JSONObject o = (JSONObject) JSON.toJSON(files.get(0));
        dlink = o.get("dlink").toString();
        isOk = true;
        querySeconds = System.currentTimeMillis() / 1000;
    }

    public void startDownload() throws IOException {
        if(!isOk){
            return;
        }
        if(localPath != null && localPath != ""){
            return;
        }
        String url = String.format("%s&access_token=%s", dlink, accessToken);
        JSONObject headers = new JSONObject();
        headers.put("User-Agent", "pan.baidu.com");
        JSONObject result = HttpRequest.downloadFile(url, headers, fileName, true);
        if(result.get("path") == null){
            return;
        }
        localPath = result.get("path").toString();
    }

    public String getFilePath(){
        if(isOk){
            return localPath;
        }
        return "";
    }
}